/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Initial Developer of the Original Code is
*   Nick Kreeger
* Portions created by the Initial Developer are Copyright (C) 2006-2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*   Nick Kreeger <nick.kreeger@park.edu>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */

#import "AccountManagerDlgController.h"
#import "MessageAccount.h"
#import "MessageAccountManager.h"
#import "NSString+Utils.h"
#import "CHISupportsOwner.h"

#import "nsCOMPtr.h"
#import "nsIMsgAccount.h"
#import "nsIMsgIdentity.h"
#import "nsIMsgIncomingServer.h"
#import "nsIMsgAccountManager.h"
#import "nsIMessengerMigrator.h"
#import "nsISmtpService.h"
#import "nsISmtpServer.h"

#import "nsMsgBaseCID.h"
#import "nsMsgCompCID.h"

@class SmtpServerItem;

typedef enum {
  eAccountOverview,
  eServerSettings,
  eSecuritySettings,
  eOutgoingServerSettings,
  eNoSettings
} ESettingsPageType;

/**
 * Define the entity for the tree structure that
 * is used in the account outline view.
 */
@interface AccountSettingsItem : NSObject
{
  AccountSettingsItem* mParent;
  NSMutableArray*      mChildren;
  NSString*            mDisplayName;
  ESettingsPageType    mPageType;
  nsISupports*         mRepGeckoItem;
}

-(id)initWithGeckoItem:(nsISupports*)aGeckoItem;
-(id)initWithPageType:(ESettingsPageType)aPageType;

-(int)numberOfChildren;
-(BOOL)isGroup;
-(AccountSettingsItem*)childAtIndex:(int)inIndex;
-(void)addChild:(AccountSettingsItem*)inChild;
-(void)removeChild:(AccountSettingsItem*)inChild;
-(void)setParent:(AccountSettingsItem*)inParent;
-(void)setPageType:(ESettingsPageType)aSettingsPageType;
-(ESettingsPageType)pageType;
-(NSString*)displayName;
-(void)setDisplayName:(NSString*)aDisplayName;
-(AccountSettingsItem*)parent;
-(nsISupports*)representedGeckoItem;
-(void)removeAllChildren;


@end

@implementation AccountSettingsItem

-(id)init
{
  if ((self = [super init]))
  {
    mParent = nil;
    mChildren = nil;
    mDisplayName = [[NSString alloc] init];
    mPageType = eNoSettings;
    mRepGeckoItem = nsnull;
  }
  
  return self;
}

-(id)initWithGeckoItem:(nsISupports*)aGeckoItem
{
  if ((self = [self init]))
  {
    // Hrm. I don't think I should addref.
    mRepGeckoItem = aGeckoItem;
  }
  
  return self;
}

-(id)initWithPageType:(ESettingsPageType)aPageType
{
  if ((self = [self init]))
  {
    [self setPageType:aPageType];
  }
  
  return self;
}

-(void)dealloc
{
  if (mChildren)
    [mChildren release];
  if (mDisplayName)
    [mDisplayName release];
  
  NS_IF_RELEASE(mRepGeckoItem);
  
  [super dealloc];
}

-(int)numberOfChildren
{
  if (mChildren)
    return [mChildren count];
  
  return 0;
}

-(BOOL)isGroup
{
  if (mChildren && [mChildren count] > 0)
    return YES;
  
  return NO;
}

-(AccountSettingsItem*)childAtIndex:(int)inIndex
{
  return [mChildren objectAtIndex:inIndex];
}

-(void)addChild:(AccountSettingsItem*)inChild
{
  if (!mChildren)
    mChildren = [[NSMutableArray alloc] init];
  
  [inChild setParent:self];
  [mChildren addObject:inChild];
}

-(void)removeChild:(AccountSettingsItem*)inChild
{
  if (!mChildren)
    return;
  
  [mChildren removeObject:inChild];  // I'm pretty sure the item gets released...
}

-(void)setParent:(AccountSettingsItem*)inParent
{
  if (mParent)
    [mParent release];
  
  mParent = [inParent retain];
}

-(void)setPageType:(ESettingsPageType)aSettingsPageType
{
  mPageType = aSettingsPageType;
  switch (mPageType)
  {
    case eServerSettings:
      mDisplayName = NSLocalizedString(@"ServerSettings", nil);
      break;
    case eSecuritySettings:
      mDisplayName = NSLocalizedString(@"SecuritySettings", nil);
      break;
    case eOutgoingServerSettings:
      mDisplayName = NSLocalizedString(@"OutgoingServerSMTP", nil);
      break;
      //XXX add the other types as they are created.
  }
}

-(ESettingsPageType)pageType
{
  return mPageType;
}

-(NSString*)displayName
{
  return mDisplayName;
}

-(void)setDisplayName:(NSString*)aDisplayName
{
  if (mDisplayName)
    [mDisplayName release];
  
  mDisplayName = [aDisplayName retain];
}

-(AccountSettingsItem*)parent
{
  return mParent;
}

-(nsISupports*)representedGeckoItem
{
  return mRepGeckoItem;
}

-(void)removeAllChildren
{
  if (!mChildren)
    return;
  
  [mChildren removeAllObjects];
}

@end

#pragma mark -

@interface AccountManagerDlgController(Private)

-(void)createAccountDataSource;
-(void)addAppropriateChildrenToItem:(AccountSettingsItem*)aSettingsItem 
                     forAccountType:(EAccountType)aAccountType;

@end

@implementation AccountManagerDlgController

static AccountManagerDlgController* sAccountManagerInstance = nil;

+(id)sharedInstance
{
  if (!sAccountManagerInstance)
    sAccountManagerInstance = [[self alloc] init];
  
  return sAccountManagerInstance;
}

-(id)init
{
  if ((self = [super init]))
  {
    mRootSettingsItem = [[AccountSettingsItem alloc] init];
    mCurView = nil;
    [self createAccountDataSource];
    [NSBundle loadNibNamed:@"AccountManager" owner:self];
  }
  
  return self;
}

-(void)awakeFromNib
{
}

-(void)showAccountManager:(NSWindow*)aParentWindow
{
  [NSApp beginSheet:mWindow
     modalForWindow:aParentWindow 
      modalDelegate:self
     didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
        contextInfo:nil];
}

-(void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
  [sheet orderOut:self];
}

-(void)createAccountDataSource
{
  //if (mRootSettingsItem)
  //  [mRootSettingsItem removeAllChildren];
  
  nsresult rv;
  nsCOMPtr<nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
  if (NS_FAILED(rv))
    return;
  
  nsCOMPtr<nsISupportsArray> accounts;
  rv = accountManager->GetAccounts(getter_AddRefs(accounts));
  if (NS_FAILED(rv))
    return;
  
  PRUint32 arrSize, i;
  rv = accounts->Count(&arrSize);
  // Don't build the tree if there aren't any accounts.
  if (NS_FAILED(rv) || arrSize == 0)  
    return;
  
  nsCOMPtr<nsIMsgIncomingServer> localFolderServer;
  rv = accountManager->GetLocalFoldersServer(getter_AddRefs(localFolderServer));
  
  for (i = 0; i < arrSize; i++)
  {
    EAccountType curAccountType = eOtherType;
    nsIMsgAccount* curAccount = NS_STATIC_CAST(nsIMsgAccount*, accounts->ElementAt(i));
    if (!curAccount)
      continue;
    
    // set name == server->prettyName
    AccountSettingsItem* curSettingsItem = [[AccountSettingsItem alloc] initWithGeckoItem:curAccount];
    
    nsCOMPtr<nsIMsgIncomingServer> curIncomingServer;
    rv = curAccount->GetIncomingServer(getter_AddRefs(curIncomingServer));
    // If we get here, there is a problem with the registered account, for now I'm going to continue
    if (NS_FAILED(rv) || !curIncomingServer)
      continue;
    
    // Give this item a name, use the pretty name of the incoming server for this.
    PRUnichar* accountName;
    curIncomingServer->GetPrettyName(&accountName);
    if (NS_SUCCEEDED(rv) && accountName)
      [curSettingsItem setDisplayName:[NSString stringWithPRUnichars:accountName]];
    else
      [curSettingsItem setDisplayName:@""];
      
    // We need to find the local server, so get the local folders server and compare the 
    // curAccount's incoming server to see if they are the same.
    if (curIncomingServer == localFolderServer)
      curAccountType = eLocalFolder;
    
    // This is a regular page type, assign the settings for the server type.
    // Is this IMAP or POP? How do I find this out?
    else
    {
      nsXPIDLCString serverType;
      curIncomingServer->GetType(getter_Copies(serverType));
      if (serverType == NS_LITERAL_CSTRING("pop3"))
        curAccountType = ePOP3;
      else if (serverType == NS_LITERAL_CSTRING("imap"))
        curAccountType = eIMAP;
    }
    
    [self addAppropriateChildrenToItem:curSettingsItem forAccountType:curAccountType];
    [mRootSettingsItem addChild:curSettingsItem];
    [curSettingsItem release];
  }
  
  // Now add the SMTP server item if those servers exist.
  nsCOMPtr<nsISmtpService> smtpService = do_GetService(NS_SMTPSERVICE_CONTRACTID, &rv);
  if (NS_FAILED(rv))
    return;
  
  nsCOMPtr<nsISupportsArray> smtpServers;
  rv = smtpService->GetSmtpServers(getter_AddRefs(smtpServers));
  if (NS_FAILED(rv))
    return;
  
  PRUint32 smtpArrSize;
  rv = smtpServers->Count(&smtpArrSize);
  if (NS_FAILED(rv) || smtpArrSize == 0)  
    return;

  AccountSettingsItem* smtpSettingsItem = [[AccountSettingsItem alloc] initWithPageType:eOutgoingServerSettings];
  [mRootSettingsItem addChild:smtpSettingsItem];
  [smtpSettingsItem release];
}

-(void)addAppropriateChildrenToItem:(AccountSettingsItem*)aSettingsItem 
                     forAccountType:(EAccountType)aAccountType
{
  if (aAccountType == ePOP3)
  {
    [aSettingsItem setPageType:eAccountOverview];
    
    AccountSettingsItem* serverSettings = [[AccountSettingsItem alloc] initWithPageType:eServerSettings];
    AccountSettingsItem* securitySettings = [[AccountSettingsItem alloc] initWithPageType:eSecuritySettings];
    
    [aSettingsItem addChild:serverSettings];
    [aSettingsItem addChild:securitySettings];
    
    [serverSettings release];
    [securitySettings release];
    
    //XXX Add the other stuff.
  }
  
  else if (aAccountType == eIMAP)
  {
    [aSettingsItem setPageType:eAccountOverview];
    
    AccountSettingsItem* serverSettings = [[AccountSettingsItem alloc] initWithPageType:eServerSettings];
    AccountSettingsItem* securitySettings = [[AccountSettingsItem alloc] initWithPageType:eSecuritySettings];
    
    [aSettingsItem addChild:serverSettings];
    [aSettingsItem addChild:securitySettings];
    
    [serverSettings release];
    [securitySettings release];
    
    //XXX Add everything but DiskSpace should be Disk Space & Offline
  }
  
  else if (aAccountType == eLocalFolder)
  {
    //XXX Set the view as a storage overview.
    //XXX Add the disk space item.
  }
  
  else if (aAccountType == eSMTP)
  {
    //XXX add the SMTP view.
  }
}

#pragma mark-

-(IBAction)addAccount:(id)sender
{
}

-(IBAction)setAccountAsDefault:(id)sender
{
  if (!mSelectedSettingsItem)
    return;
  
  nsresult rv;
  nsCOMPtr<nsIMsgAccount> msgAccount = do_QueryInterface([mSelectedSettingsItem representedGeckoItem], &rv);
  if (NS_SUCCEEDED(rv))
    [[MessageAccountManager sharedInstance] setDefaultMsgAccount:msgAccount];
}

-(IBAction)removeAccount:(id)sender
{
  if (!mSelectedSettingsItem)
    return;
  //XXX Pose a "are you sure" dialog here.
  //XXX Check to make sure the protocol isn't running.
  nsresult rv;
  nsCOMPtr<nsIMsgAccount> msgAccount = do_QueryInterface([mSelectedSettingsItem representedGeckoItem], &rv);
  if (![[MessageAccountManager sharedInstance] removeMsgAccount:msgAccount])
  {
    //XXX pose failure message here.
    return;
  }
  
  // Remove this item from the tree and rebuild.
  [[mSelectedSettingsItem parent] removeChild:mSelectedSettingsItem];
  
  if (mSelectedSettingsItem)
    [mSelectedSettingsItem release];  // paranoia.
  
  [mAccountOutlineView reloadData];
}

-(IBAction)closeDialog:(id)sender
{
  [mCurView saveChanges];
  [[MessageAccountManager sharedInstance] saveAccounts];
  [NSApp endSheet:[sender window]];
}

-(IBAction)cancel:(id)sender
{
  //XXX if there are changes, notify the user.
  [NSApp endSheet:[sender window]];
}

#pragma mark -

// 
// NSOutlineView Delegate Methods
//
-(BOOL)outlineView:(NSOutlineView *)inOutlineView shouldEditTableColumn:(NSTableColumn *)inTableColumn 
               item:(id)inItem
{
  return NO;
}

-(BOOL)outlineView:(NSOutlineView *)inOutlineView shouldSelectItem:(id)inItem
{
  if (mCurView)
  {
    [mCurView removeFromSuperview];
    [mCurView saveChanges];  // hold the set values in the nsISupports object.
  }
  
  AccountSettingsItem* curSettingsItem = (AccountSettingsItem*)inItem;
  NSView<AccountManagerViewDelegate>* curSettingsView = nil;
  nsISupports* curGeckoItem;
  
  switch ([curSettingsItem pageType])
  {
    case eAccountOverview:
      curGeckoItem = [curSettingsItem representedGeckoItem];
      curSettingsView = mAccountOverviewView;
      break;
    case eServerSettings:
      curGeckoItem = [[curSettingsItem parent] representedGeckoItem];
      curSettingsView = mServerSettingsView;
      break;
    case eSecuritySettings:
      curGeckoItem = [[curSettingsItem parent] representedGeckoItem];
      curSettingsView = mSecuritySettingsView;
      break;
    case eOutgoingServerSettings:
      curGeckoItem = [[curSettingsItem parent] representedGeckoItem];
      curSettingsView = mOutgoingServersView;
      break;
  }
  
  if (curSettingsView)
  {
    [curSettingsView updateWithSupportsItem:curGeckoItem];
    [mSettingsView addSubview:curSettingsView];
  }
  
  [mRemoveAccountButton setEnabled:(curSettingsView == mAccountOverviewView)];
  
  mCurView = curSettingsView;
  mSelectedSettingsItem = curSettingsItem;
  
  return YES;
}

//
// NSOutlineViewDataSource Protocol Methods
//
-(id)outlineView:(NSOutlineView*)inOutlineView child:(int)inIndex ofItem:(id)inItem 
{
  AccountSettingsItem* curItem = (AccountSettingsItem*)(inItem ? inItem : mRootSettingsItem);
  return [curItem childAtIndex:inIndex];
}

-(id)outlineView:(NSOutlineView *)inOutlineView objectValueForTableColumn:(NSTableColumn *)inTableColumn 
          byItem:(id)inItem 
{
  // Since we only have one table column (for now). Just apply the name of the object.
  return [inItem displayName];
}

-(int)outlineView:(NSOutlineView *)inOutlineView numberOfChildrenOfItem:(id)inItem 
{
  // For some reason, we don't point this thing at the correct start index.
  // For that reason, we want to check to see if inItem is nil, if it is, get
  // the number of children for our top level item.
  id item = nil;
  if (!inItem)
    item = mRootSettingsItem;
  else
    item = inItem;
  
  return [item numberOfChildren];
}

-(BOOL)outlineView:(NSOutlineView *)inOutlineView isItemExpandable:(id)inItem 
{
  if (inItem != nil && [inItem isGroup])
    return YES;
  
  return NO;
}

@end

#pragma mark -

@interface AccountOverviewView(Private)

-(NSString*)geckoAccountName;
-(NSString*)geckoFullName;
-(NSString*)geckoEmailAddress;
-(NSString*)geckoReplyToAddress;
-(NSString*)geckoOrganization;
-(nsIMsgIdentity*)representedMsgIdentity;

@end

@implementation AccountOverviewView

-(void)dealloc
{
  NS_IF_RELEASE(mSupportsObject);
  [super dealloc];
}

-(void)updateWithSupportsItem:(nsISupports*)aSupportsItem
{
  NS_IF_ADDREF(aSupportsItem);
  mSupportsObject = aSupportsItem;
  
  [mAccountNameField setStringValue:[self geckoAccountName]];
  [mYourNameField setStringValue:[self geckoFullName]];
  [mEmailAddressField setStringValue:[self geckoEmailAddress]];
  [mReplyToAddressField setStringValue:[self geckoReplyToAddress]];
  [mOrganizationField setStringValue:[self geckoOrganization]];
  
  // Find the default SMTP server for this identity.
  SmtpServerItem* defaultSmtpItem = [[MessageAccountManager sharedInstance] defaultSmtpServerItemForIdentity:[self representedMsgIdentity]];
  NSMenu* smtpMenu = [[[NSMenu alloc] init] autorelease];
  NSArray* smtpServerList = [[MessageAccountManager sharedInstance] smtpServerList];
  NSEnumerator* smtpServersEnum = [smtpServerList objectEnumerator];
  SmtpServerItem* curSmtpServerItem = nil;
  while ((curSmtpServerItem = [smtpServersEnum nextObject]))
  {
    NSMenuItem* menuItem = [[[NSMenuItem alloc] initWithTitle:[curSmtpServerItem displayName] 
                                                      action:nil
                                                keyEquivalent:@""] autorelease];
    [menuItem setTarget:self];
    [menuItem setRepresentedObject:curSmtpServerItem];
    [smtpMenu addItem:menuItem];
  }
  
  [smtpServerList release];
  
  [mOutgoingSMTPPopup setMenu:smtpMenu];
  [mOutgoingSMTPPopup selectItemWithTitle:[defaultSmtpItem displayName]];
}

-(void)saveChanges
{
  nsresult rv;
  nsIMsgIdentity* identity = [self representedMsgIdentity];
  if (!identity)
    return;
  
  if (![[mAccountNameField stringValue] isEqualToString:[self geckoAccountName]])
  {
    nsCOMPtr<nsIMsgAccount> account = do_QueryInterface(mSupportsObject, &rv);
    if (NS_SUCCEEDED(rv))
    {
      nsCOMPtr<nsIMsgIncomingServer> incomingServer;
      rv = account->GetIncomingServer(getter_AddRefs(incomingServer));
      if (NS_SUCCEEDED(rv))
        incomingServer->SetPrettyName([[mAccountNameField stringValue] createNewUnicodeBuffer]); 
    }
  }
  if (![[mYourNameField stringValue] isEqualToString:[self geckoFullName]])
    identity->SetFullName([[mYourNameField stringValue] createNewUnicodeBuffer]);
  
  if (![[mEmailAddressField stringValue] isEqualToString:[self geckoEmailAddress]])
    identity->SetEmail([[mEmailAddressField stringValue] cString]);
  
  if (![[mReplyToAddressField stringValue] isEqualToString:[self geckoReplyToAddress]])
    identity->SetReplyTo([[mReplyToAddressField stringValue] cString]);
  
  if (![[mOrganizationField stringValue] isEqualToString:[self geckoOrganization]])
    identity->SetOrganization([[mOrganizationField stringValue] createNewUnicodeBuffer]);
  
  SmtpServerItem* defaultSmtpItem = [[MessageAccountManager sharedInstance] defaultSmtpServerItemForIdentity:[self representedMsgIdentity]];
  SmtpServerItem* selectedSmtpItem = [[mOutgoingSMTPPopup selectedItem] representedObject];
  if (![defaultSmtpItem isEqualTo:selectedSmtpItem])
  {
    nsXPIDLCString smtpKey;
    nsISmtpServer* smtpServer = [selectedSmtpItem smtpServer];
    rv = smtpServer->GetKey(getter_Copies(smtpKey));
    if (NS_SUCCEEDED(rv))
      identity->SetSmtpServerKey(smtpKey);
  }
}

-(NSString*)geckoAccountName
{
  nsresult rv;
  nsCOMPtr<nsIMsgAccount> account = do_QueryInterface(mSupportsObject, &rv);
  NS_ENSURE_SUCCESS(rv, [NSString stringWithString:@""]);
  
  nsCOMPtr<nsIMsgIncomingServer> incomingServer;
  rv = account->GetIncomingServer(getter_AddRefs(incomingServer));
  NS_ENSURE_SUCCESS(rv, [NSString stringWithString:@""]);
  
  PRUnichar* accountName;
  rv = incomingServer->GetPrettyName(&accountName);
  if (NS_FAILED(rv) || !accountName)
    return [NSString stringWithString:@""];
  
  return [NSString stringWithPRUnichars:accountName];
}

-(NSString*)geckoFullName
{
  nsresult rv;
  nsIMsgIdentity* identity = [self representedMsgIdentity];
  if (!identity)
    return [NSString stringWithString:@""];
  
  PRUnichar* fullName;
  rv = identity->GetFullName(&fullName);
  if (NS_FAILED(rv) || !fullName)
    return [NSString stringWithString:@""];
  
  return [NSString stringWithPRUnichars:fullName];
}

-(NSString*)geckoEmailAddress
{
  nsresult rv;
  nsIMsgIdentity* identity = [self representedMsgIdentity];
  if (!identity)
    return [NSString stringWithString:@""];
  
  nsXPIDLCString email;
  rv = identity->GetEmail(getter_Copies(email));
  if (NS_FAILED(rv) || !email)
    return [NSString stringWithString:@""];
  
  return [NSString stringWithCString:email];
}

-(NSString*)geckoReplyToAddress
{
  nsresult rv;
  nsIMsgIdentity* identity = [self representedMsgIdentity];
  if (!identity)
    return [NSString stringWithString:@""];
  
  nsXPIDLCString replyToAddr;
  rv = identity->GetReplyTo(getter_Copies(replyToAddr));
  if (NS_FAILED(rv) || !replyToAddr)
    return [NSString stringWithString:@""];
  
  return [NSString stringWithCString:replyToAddr];
}

-(NSString*)geckoOrganization
{
  nsresult rv;
  nsIMsgIdentity* identity = [self representedMsgIdentity];
  if (!identity)
    return [NSString stringWithString:@""];
  
  PRUnichar* org;
  rv = identity->GetOrganization(&org);
  if (NS_FAILED(rv) || !org)
    return [NSString stringWithString:@""];
  
  return [NSString stringWithPRUnichars:org];
}

-(nsIMsgIdentity*)representedMsgIdentity
{
  nsresult rv;
  nsCOMPtr<nsIMsgAccount> account = do_QueryInterface(mSupportsObject, &rv);
  NS_ENSURE_SUCCESS(rv, nsnull);
  
  nsCOMPtr<nsIMsgIdentity> identity;
  rv = account->GetDefaultIdentity(getter_AddRefs(identity));
  NS_ENSURE_SUCCESS(rv, nsnull);
  
  return identity;
}

@end

#pragma mark -

@interface ServerSettingsView(Private)

-(NSString*)serverType;
-(NSString*)geckoServerName;
-(NSString*)geckoUserName;
-(int)geckoPortNum;
-(BOOL)geckoShouldCheckForMessages;
-(int)geckoCheckTimeDelay;
-(BOOL)geckoShouldAutoDownloadMessages;
-(BOOL)geckoShouldEmptyTrashOnExit;
-(nsIMsgIncomingServer*)representedIncomingServer;

//XXX add the other crap here.

@end

@implementation ServerSettingsView 

-(void)dealloc
{
  NS_IF_RELEASE(mSupportsObject);
  [super dealloc];
}

-(void)updateWithSupportsItem:(nsISupports*)aSupportsItem
{
  NS_IF_ADDREF(aSupportsItem);
  mSupportsObject = aSupportsItem;
  
  [mServerTypeField setStringValue:[self serverType]];
  [mServerNameField setStringValue:[self geckoServerName]];
  [mUserNameField setStringValue:[self geckoUserName]];
  [mPortField setIntValue:[self geckoPortNum]];
  [mCheckForMessagesOnTimerCheckbox setState:[self geckoShouldCheckForMessages]];
  [mTimerMinutesFieldCheckbox setIntValue:[self geckoCheckTimeDelay]];
  [mAutoDownloadNewMessages setState:[self geckoShouldAutoDownloadMessages]];
  [mEmptyTrashOnExitCheckbox setState:[self geckoShouldEmptyTrashOnExit]];
}

-(void)saveChanges
{
  nsIMsgIncomingServer* incomingServer = [self representedIncomingServer];
  if (!incomingServer)
    return;
  
  if (![[mServerNameField stringValue] isEqualToString:[self geckoServerName]])
    incomingServer->SetHostName([[mServerNameField stringValue] cString]);
  
  if (![[mUserNameField stringValue] isEqualToString:[self geckoUserName]])
    incomingServer->SetUsername([[mUserNameField stringValue] cString]);
  
  if ([mPortField intValue] != [self geckoPortNum])  //XXX ensure that this is an int...
    incomingServer->SetPort([mPortField intValue]);
  
  if ([mCheckForMessagesOnTimerCheckbox state] != [self geckoShouldCheckForMessages])
    incomingServer->SetDoBiff([mCheckForMessagesOnTimerCheckbox state]);
  
  if ([mTimerMinutesFieldCheckbox isEnabled] && [mTimerMinutesFieldCheckbox intValue] != [self geckoCheckTimeDelay])
    incomingServer->SetBiffMinutes([mTimerMinutesFieldCheckbox intValue]);
  
  if ([mAutoDownloadNewMessages state] != [self geckoShouldAutoDownloadMessages])
    incomingServer->SetDownloadOnBiff([mAutoDownloadNewMessages state]);
  
  if ([mEmptyTrashOnExitCheckbox state] != [self geckoShouldEmptyTrashOnExit])
    incomingServer->SetEmptyTrashOnExit([mEmptyTrashOnExitCheckbox state]);
}

-(NSString*)serverType
{
  nsresult rv;
  nsIMsgIncomingServer* incomingServer = [self representedIncomingServer];
  if (!incomingServer)
    return [NSString stringWithString:@"Mail Server"];
  
  nsXPIDLCString serverType;
  rv = incomingServer->GetType(getter_Copies(serverType));
  if (NS_FAILED(rv) || !serverType)
    return [NSString stringWithString:@"Mail Server"];
  
  return [NSString stringWithFormat:@"%@ Mail Server", [[NSString stringWithCString:serverType] uppercaseString]];
}

-(NSString*)geckoServerName
{
  nsresult rv;
  nsIMsgIncomingServer* incomingServer = [self representedIncomingServer];
  if (!incomingServer)
    return [NSString stringWithString:@""];
  
  nsXPIDLCString serverName;
  rv = incomingServer->GetHostName(getter_Copies(serverName));
  if (NS_FAILED(rv) || !serverName)
    return [NSString stringWithString:@""];
  
  return [NSString stringWithCString:serverName];
}

-(NSString*)geckoUserName
{
  nsresult rv;
  nsIMsgIncomingServer* incomingServer = [self representedIncomingServer];
  if (!incomingServer)
    return [NSString stringWithString:@""];
  
  nsXPIDLCString userName;
  rv = incomingServer->GetUsername(getter_Copies(userName));
  if (NS_FAILED(rv) || !userName)
    return [NSString stringWithString:@""];
  
  return [NSString stringWithCString:userName];
}

-(int)geckoPortNum
{
  nsresult rv;
  nsIMsgIncomingServer* incomingServer = [self representedIncomingServer];
  if (!incomingServer)
    return 0;
  
  PRInt32 portNum;
  rv = incomingServer->GetPort(&portNum);
  NS_ENSURE_SUCCESS(rv, 0);
  
  return portNum;
}

-(BOOL)geckoShouldCheckForMessages
{
  nsresult rv;
  nsIMsgIncomingServer* incomingServer = [self representedIncomingServer];
  if (!incomingServer)
    return NO;
  
  PRBool shouldCheckForMessages;
  rv = incomingServer->GetDoBiff(&shouldCheckForMessages);
  NS_ENSURE_SUCCESS(rv, shouldCheckForMessages);
  
  return shouldCheckForMessages;
}

-(int)geckoCheckTimeDelay
{
  nsresult rv;
  nsIMsgIncomingServer* incomingServer = [self representedIncomingServer];
  if (!incomingServer)
    return 0;
  
  PRInt32 timeDelay;
  rv = incomingServer->GetBiffMinutes(&timeDelay);
  NS_ENSURE_SUCCESS(rv, 0);
  
  return timeDelay;
}

-(BOOL)geckoShouldAutoDownloadMessages
{
  nsresult rv;
  nsIMsgIncomingServer* incomingServer = [self representedIncomingServer];
  if (!incomingServer)
    return NO;
  
  PRBool shouldAutoDownload;
  rv = incomingServer->GetDownloadOnBiff(&shouldAutoDownload);
  NS_ENSURE_SUCCESS(rv, NO);
  
  return shouldAutoDownload;
}

-(BOOL)geckoShouldEmptyTrashOnExit
{
  nsresult rv;
  nsIMsgIncomingServer* incomingServer = [self representedIncomingServer];
  if (!incomingServer)
    return NO;
  
  PRBool emptyTrashOnExit;
  rv = incomingServer->GetEmptyTrashOnExit(&emptyTrashOnExit);
  NS_ENSURE_SUCCESS(rv, NO);
  
  return emptyTrashOnExit;
}

-(nsIMsgIncomingServer*)representedIncomingServer
{
  nsresult rv;
  nsCOMPtr<nsIMsgAccount> account = do_QueryInterface(mSupportsObject, &rv);
  NS_ENSURE_SUCCESS(rv, nsnull);
  
  nsCOMPtr<nsIMsgIncomingServer> incomingServer;
  rv = account->GetIncomingServer(getter_AddRefs(incomingServer));
  NS_ENSURE_SUCCESS(rv, nsnull);
  
  return incomingServer;
}

-(IBAction)didClickAutoDownloadCheckbox:(id)sender
{
  [mTimerMinutesFieldCheckbox setEnabled:[mCheckForMessagesOnTimerCheckbox state]];
}

@end

#pragma mark -

@interface SecuritySettingsView(Private)

-(int)geckoSocketType;
-(BOOL)geckoUseSecureAuth;
-(nsIMsgIncomingServer*)representedIncomingServer;

@end

@implementation SecuritySettingsView

-(void)dealloc
{
  NS_IF_RELEASE(mSupportsObject);
  [super dealloc];
}

-(void)updateWithSupportsItem:(nsISupports*)aSupportsItem
{
  NS_IF_ADDREF(aSupportsItem);
  mSupportsObject = aSupportsItem;
  
  [mSecureConnectionTypeMatrix selectCellWithTag:[self geckoSocketType]];
  [mUseSecureAuthButton setState:[self geckoUseSecureAuth]];
}

-(void)saveChanges
{
  nsIMsgIncomingServer* incomingServer = [self representedIncomingServer];
  if (!incomingServer)
    return;
  
  if ([mSecureConnectionTypeMatrix selectedTag] != [self geckoSocketType])
    incomingServer->SetSocketType([mSecureConnectionTypeMatrix selectedTag]);
  
  if ([mUseSecureAuthButton state] != [self geckoUseSecureAuth])
    incomingServer->SetIsSecure([mUseSecureAuthButton state]);
}

-(int)geckoSocketType
{
  nsresult rv;
  nsIMsgIncomingServer* incomingServer = [self representedIncomingServer];
  if (!incomingServer)
    return 0;
  
  PRInt32 socketType;
  rv = incomingServer->GetSocketType(&socketType);
  NS_ENSURE_SUCCESS(rv, 0);
  
  return socketType;
}

-(BOOL)geckoUseSecureAuth
{
  nsresult rv;
  nsIMsgIncomingServer* incomingServer = [self representedIncomingServer];
  if (!incomingServer)
    return NO;
  
  PRBool useSecureAuth;
  rv = incomingServer->GetIsSecure(&useSecureAuth);
  if (NS_FAILED(rv))
    return NO;
  
  return useSecureAuth;
}

-(nsIMsgIncomingServer*)representedIncomingServer
{
  nsresult rv;
  nsCOMPtr<nsIMsgAccount> account = do_QueryInterface(mSupportsObject, &rv);
  NS_ENSURE_SUCCESS(rv, nsnull);
  
  nsCOMPtr<nsIMsgIncomingServer> incomingServer;
  rv = account->GetIncomingServer(getter_AddRefs(incomingServer));
  NS_ENSURE_SUCCESS(rv, nsnull);
  
  return incomingServer;
}

@end

#pragma mark -

@interface OutgoingServersView(Private)

-(void)setupView;
-(void)updateServerInfoView;
-(nsISmtpServer*)curSelectedSmtpServer;
-(void)descriptionFieldDidChange:(NSNotification*)aNotification;
-(void)serverNameFieldDidChange:(NSNotification*)aNotification;
-(void)portFieldDidChange:(NSNotification*)aNotification;
-(void)usernameFieldDidChange:(NSNotification*)aNotification;

@end

@implementation OutgoingServersView

-(void)awakeFromNib
{
  mHasSetup = NO;
}

-(void)dealloc
{
  if (mSmtpServers)
    [mSmtpServers release];
  
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  
  [super dealloc];
}

-(IBAction)addServer:(id)sender
{
  //XXX write me!
}

-(IBAction)removeServer:(id)sender
{
  //XXX show prompt.
  
  // Selection is forced in IB.
  SmtpServerItem* selectedServerItem = (SmtpServerItem*)[mSmtpServers objectAtIndex:[mServersTableView selectedRow]];
  if (!selectedServerItem)
    return;
  
  nsresult rv;
  nsCOMPtr<nsISmtpService> smtpService = do_GetService(NS_SMTPSERVICE_CONTRACTID, &rv);
  if (NS_FAILED(rv))
    return;
  
  rv = smtpService->DeleteSmtpServer([selectedServerItem smtpServer]);
  if (NS_SUCCEEDED(rv))
  {
    [[MessageAccountManager sharedInstance] saveAccounts];
    [mSmtpServers removeObject:selectedServerItem];
    [mServersTableView reloadData];
  }
}

-(IBAction)setDefaultServer:(id)sender
{
  nsresult rv;
  nsCOMPtr<nsISmtpService> smtpService = do_GetService(NS_SMTPSERVICE_CONTRACTID, &rv);
  if (NS_FAILED(rv) || !smtpService)
    return;
  
  nsCOMPtr<nsISmtpServer> selectedServer = [self curSelectedSmtpServer];
  if (selectedServer)
  {
    char* hostname;
    selectedServer->GetHostname(&hostname);
    smtpService->SetDefaultServer(selectedServer);
  }
  
  [self updateWithSupportsItem:nsnull];  // force a refresh
}

-(IBAction)setUseAuthentication:(id)sender
{
  [mCurSelectedServerItem setUseAuthentication:[mUseUserAndPassCheckbox state]];
  if ([mUseUserAndPassCheckbox state] == NSOnState)
  {
    [mUserNameField setEnabled:YES];
    [mUserNameField setStringValue:[mCurSelectedServerItem username]];
  }
  else
  {
    [mUserNameField setEnabled:NO];
    [mUserNameField setStringValue:@""];
  }
}

-(IBAction)setSecureConnectionType:(id)sender
{
  [mCurSelectedServerItem setSocketType:[mSecureConnectionMatrix selectedTag]];
}

-(void)setupView
{
  [mServersTableView setDelegate:self];
  
  [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(descriptionFieldDidChange:) 
                                               name:NSControlTextDidEndEditingNotification 
                                             object:mDescriptionField];
  
  [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(serverNameFieldDidChange:) 
                                               name:NSControlTextDidEndEditingNotification 
                                             object:mServerNameField];
  
  [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(portFieldDidChange:) 
                                               name:NSControlTextDidEndEditingNotification 
                                             object:mPortField];
  
  [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(usernameFieldDidChange:) 
                                               name:NSControlTextDidEndEditingNotification 
                                             object:mUserNameField];
  
  mHasSetup = YES;
}

-(void)updateWithSupportsItem:(nsISupports*)aSupportsItem
{
  if (!mHasSetup)
    [self setupView];
  
  mSmtpServers = [[MessageAccountManager sharedInstance] smtpServerList];
  
  [mServersTableView setDataSource:self];
  [mServersTableView reloadData];
  
  // If this is the intial load of the view, the first server will be seleced
  if (!mCurSelectedServerItem && [mSmtpServers count] > 0)
    mCurSelectedServerItem = [mSmtpServers objectAtIndex:0];
  
  [self updateServerInfoView];
}

-(void)saveChanges
{
  NSEnumerator* serversEnum = [mSmtpServers objectEnumerator];
  SmtpServerItem* curServer = nil;
  while ((curServer = [serversEnum nextObject]))
    [curServer saveSettings];
}

-(void)updateServerInfoView
{
  if (mHasSetup)
  {
    [mDescriptionField setStringValue:[mCurSelectedServerItem description]];
    [mServerNameField setStringValue:[mCurSelectedServerItem serverName]];
    [mPortField setIntValue:[mCurSelectedServerItem portNum]];
    [mUseUserAndPassCheckbox setState:[mCurSelectedServerItem useAuthentication]];
    
    if ([mUseUserAndPassCheckbox state] == NSOffState)
    {
      [mUserNameField setStringValue:@""];
      [mUserNameField setEnabled:NO];
    }
    else
    {
      [mUserNameField setEnabled:YES];
      [mUserNameField setStringValue:[mCurSelectedServerItem username]];
    }
    [mSecureConnectionMatrix selectCellWithTag:[mCurSelectedServerItem socketType]];
  }
  else
  {
    [mDescriptionField setStringValue:@""];
    [mServerNameField setStringValue:@""];
    [mPortField setStringValue:@""];
    [mUseUserAndPassCheckbox setState:NSOffState];
    [mUserNameField setStringValue:@""];
    [mSecureConnectionMatrix selectCellWithTag:0];
  }
}

-(nsISmtpServer*)curSelectedSmtpServer
{
  return [mCurSelectedServerItem smtpServer];
}

//
// NSTableDataSource Protocol Methods
// 
- (id)tableView:(NSTableView*)inTableView
      objectValueForTableColumn:(NSTableColumn*)inTableColumn
            row:(int)inRowIndex
{
  if  (!mSmtpServers)
    return nil;
  
  // There is only one table column, so just build a pretty string that
  // follows this format:
  //   "emailAddr - server |(Default)|"
  SmtpServerItem* curSmtpServerItem = (SmtpServerItem*)[mSmtpServers objectAtIndex:inRowIndex];
  
  return [curSmtpServerItem displayName];
}

-(int)numberOfRowsInTableView:(NSTableView*)inTableView
{
  if (mSmtpServers)
    return [mSmtpServers count];
  
  return 0;
}

-(BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(int)rowIndex
{
  mCurSelectedServerItem = [mSmtpServers objectAtIndex:rowIndex];
  [self updateServerInfoView];
  return YES;
}

-(void)descriptionFieldDidChange:(NSNotification*)aNotification
{
  [mCurSelectedServerItem setDescription:[mDescriptionField stringValue]];
}

-(void)serverNameFieldDidChange:(NSNotification*)aNotification
{
  [mCurSelectedServerItem setServerName:[mServerNameField stringValue]];
}

-(void)portFieldDidChange:(NSNotification*)aNotification
{
  [mCurSelectedServerItem setPortNum:[mPortField intValue]];
}

-(void)usernameFieldDidChange:(NSNotification*)aNotification
{
  [mCurSelectedServerItem setUsername:[mUserNameField stringValue]];
}

@end
